package cn.iocoder.yudao.module.promotion.service.yearcardorder;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.iocoder.yudao.framework.common.core.KeyValue;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.member.api.user.MemberUserApi;
import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
import cn.iocoder.yudao.module.pay.api.order.PayOrderApi;
import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderCreateReqDTO;
import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderRespDTO;
import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum;
import cn.iocoder.yudao.module.promotion.api.coupon.CouponApi;
import cn.iocoder.yudao.module.promotion.controller.admin.yearcardorder.vo.YearCardOrderPageReqVO;
import cn.iocoder.yudao.module.promotion.controller.admin.yearcardorder.vo.YearCardOrderSaveReqVO;
import cn.iocoder.yudao.module.promotion.controller.app.yearcardorder.config.YearCardOrderProperties;
import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.yearcardconfig.YearCardConfigDO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.yearcardorder.YearCardOrderDO;
import cn.iocoder.yudao.module.promotion.dal.mysql.coupon.CouponMapper;
import cn.iocoder.yudao.module.promotion.dal.mysql.yearcardorder.YearCardOrderMapper;
import cn.iocoder.yudao.module.promotion.dal.redis.no.YearCardOrderNoRedisDAO;
import cn.iocoder.yudao.module.promotion.enums.DictTypeConstants;
import cn.iocoder.yudao.module.promotion.enums.coupon.CouponTakeTypeEnum;
import cn.iocoder.yudao.module.promotion.service.yearcardconfig.YearCardConfigService;
import cn.iocoder.yudao.module.system.api.dict.DictDataApi;
import cn.iocoder.yudao.module.system.api.dict.dto.DictDataRespDTO;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.sun.org.apache.xpath.internal.operations.Bool;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import javax.validation.constraints.NotNull;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.addTime;
import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.*;
import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.*;

/**
 * 年卡订单 Service 实现类
 *
 * @author 万民匠心
 */
@Slf4j
@Service
@Validated
public class YearCardOrderServiceImpl implements YearCardOrderService {

    @Resource
    private YearCardOrderMapper yearCardOrderMapper;
    @Resource
    private YearCardOrderNoRedisDAO yearCardOrderNoRedisDAO;
    @Resource
    private MemberUserApi memberUserApi;
    @Resource
    private YearCardConfigService yearCardConfigService;
    @Resource
    private PayOrderApi payOrderApi;
    @Resource
    private YearCardOrderProperties yearCardOrderProperties;
    @Resource
    private CouponApi couponApi;
    @Resource
    private DictDataApi dictDataApi;
    @Resource
    private CouponMapper couponMapper;

    @Override
    public Long createYearCardOrder(YearCardOrderSaveReqVO createReqVO) {
        // 插入
        YearCardOrderDO yearCardOrder = BeanUtils.toBean(createReqVO, YearCardOrderDO.class);
        yearCardOrderMapper.insert(yearCardOrder);
        // 返回
        return yearCardOrder.getId();
    }

    @Override
    public void updateYearCardOrder(YearCardOrderSaveReqVO updateReqVO) {
        // 校验存在
        validateYearCardOrderExists(updateReqVO.getId());
        // 更新
        YearCardOrderDO updateObj = BeanUtils.toBean(updateReqVO, YearCardOrderDO.class);
        yearCardOrderMapper.updateById(updateObj);
    }

    @Override
    public void updateOrderPaid(Long id, Long payOrderId) {
        KeyValue<YearCardOrderDO, PayOrderRespDTO> orderResult = validateOrderPayable(id, payOrderId);
        YearCardOrderDO yearCardOrderDO = orderResult.getKey();
        yearCardOrderDO.setPayOrderStatus(1);
        if (yearCardOrderDO.getYearCardStartTime().isBefore(LocalDateTime.now())
                && yearCardOrderDO.getYearCardEndTime().isAfter(LocalDateTime.now())) {
            yearCardOrderDO.setStatus(Boolean.TRUE);
        }
        yearCardOrderMapper.updateById(yearCardOrderDO);

        // 发券
        DictDataRespDTO dictDataRespDTO = dictDataApi.parseDictData(DictTypeConstants.YEAR_CARD_COUPON, DictTypeConstants.YEAR_CARD_COUPON_LABEL);
        if (dictDataRespDTO == null) {
            log.error("未配置成为年卡会员后发券");
        } else {
            log.info("成为年卡会员发券, couponTemplateId:{}, memberId:{}", dictDataRespDTO.getValue(), yearCardOrderDO.getMemberId());
            couponApi.takeCoupon(Long.valueOf(dictDataRespDTO.getValue()), Sets.newHashSet(yearCardOrderDO.getMemberId()), CouponTakeTypeEnum.ADMIN, Boolean.FALSE, null ,null);
        }
    }

    @NotNull
    private YearCardOrderDO validateOrderExists(Long id) {
        // 校验订单是否存在
        YearCardOrderDO order = yearCardOrderMapper.selectById(id);
        if (order == null) {
            throw exception(YEAR_CARD_ORDER_NOT_EXISTS);
        }
        return order;
    }

    private KeyValue<YearCardOrderDO, PayOrderRespDTO> validateOrderPayable(Long id, Long payOrderId) {
        // 校验订单是否存在
        YearCardOrderDO order = validateOrderExists(id);
        // 校验订单未支付
        if (!Objects.equals(0, order.getPayOrderStatus())) {
            log.error("[validateOrderPaid][order({}) 不处于待支付状态，请进行处理！order 数据是：{}]",
                    id, JsonUtils.toJsonString(order));
            throw exception(ORDER_UPDATE_PAID_STATUS_NOT_UNPAID);
        }
        // 校验支付订单匹配
        if (ObjectUtil.notEqual(order.getPayOrderId(), payOrderId)) { // 支付单号
            log.error("[validateOrderPaid][order({}) 支付单不匹配({})，请进行处理！order 数据是：{}]",
                    id, payOrderId, JsonUtils.toJsonString(order));
            throw exception(ORDER_UPDATE_PAID_FAIL_PAY_ORDER_ID_ERROR);
        }

        // 校验支付单是否存在
        PayOrderRespDTO payOrder = payOrderApi.getOrder(payOrderId);
        if (payOrder == null) {
            log.error("[validateOrderPaid][order({}) payOrder({}) 不存在，请进行处理！]", id, payOrderId);
            throw exception(ORDER_NOT_FOUND);
        }
        // 校验支付单已支付
        if (!PayOrderStatusEnum.isSuccess(payOrder.getStatus())) {
            log.error("[validateOrderPaid][order({}) payOrder({}) 未支付，请进行处理！payOrder 数据是：{}]",
                    id, payOrderId, JsonUtils.toJsonString(payOrder));
            throw exception(ORDER_UPDATE_PAID_FAIL_PAY_ORDER_STATUS_NOT_SUCCESS);
        }
        // 校验支付金额一致
        if (ObjectUtil.notEqual(payOrder.getPrice(), order.getYearCardAmount().multiply(new BigDecimal(100)).intValue())) {
            log.error("[validateOrderPaid][order({}) payOrder({}) 支付金额不匹配，请进行处理！order 数据是：{}，payOrder 数据是：{}]",
                    id, payOrderId, JsonUtils.toJsonString(order), JsonUtils.toJsonString(payOrder));
            throw exception(ORDER_UPDATE_PAID_FAIL_PAY_PRICE_NOT_MATCH);
        }
        // 校验支付订单匹配（二次）
        if (ObjectUtil.notEqual(payOrder.getMerchantOrderId(), id.toString())) {
            log.error("[validateOrderPaid][order({}) 支付单不匹配({})，请进行处理！payOrder 数据是：{}]",
                    id, payOrderId, JsonUtils.toJsonString(payOrder));
            throw exception(ORDER_UPDATE_PAID_FAIL_PAY_ORDER_ID_ERROR);
        }
        return new KeyValue<>(order, payOrder);
    }

    @Override
    public void deleteYearCardOrder(Long id) {
        // 校验存在
        validateYearCardOrderExists(id);
        // 删除
        yearCardOrderMapper.deleteById(id);
    }

    private void validateYearCardOrderExists(Long id) {
        if (yearCardOrderMapper.selectById(id) == null) {
            throw exception(YEAR_CARD_ORDER_NOT_EXISTS);
        }
    }

    @Override
    public YearCardOrderDO getYearCardOrder(Long id) {
        return yearCardOrderMapper.selectById(id);
    }

    @Override
    public YearCardOrderDO getEnabledYearCardOrderByMemberId(Long id) {
        List<YearCardOrderDO> yearCardOrderDOS = yearCardOrderMapper.selectList(YearCardOrderDO::getMemberId, id, YearCardOrderDO::getPayOrderStatus, 1);
        return yearCardOrderDOS.stream().filter(e ->
                        LocalDateTime.now().isAfter(e.getYearCardStartTime()) && LocalDateTime.now().isBefore(e.getYearCardEndTime()))
                .findFirst().orElse(null);
    }

    @Override
    public YearCardOrderDO createOrder(Long userId, String userIp) {
        MemberUserRespDTO user = memberUserApi.getUser(userId);
        if (user == null) {
            throw exception(YEAR_CARD_ORDER_USER_NOT_EXISTS);
        }
        YearCardConfigDO cardConfig = yearCardConfigService.getYearCardConfig();
        if (cardConfig == null) {
            throw exception(YEAR_CARD_CONFIG_NOT_EXISTS);
        }
        YearCardOrderDO enabledYearCardOrder = getEnabledYearCardOrderByMemberId(userId);
        if (enabledYearCardOrder != null) {
            throw exception(YEAR_CARD_ORDER_EXISTS);
        }

        YearCardOrderDO yearCardOrderDO = new YearCardOrderDO();
        yearCardOrderDO.setOrderNo(yearCardOrderNoRedisDAO.generateNo(YearCardOrderNoRedisDAO.YEAR_CARD_ORDER_NO_PREFIX));
        yearCardOrderDO.setMemberId(userId);
        yearCardOrderDO.setNickname(user.getNickname());
        yearCardOrderDO.setMobile(user.getMobile());
        yearCardOrderDO.setYearCardAmount(cardConfig.getPromotionPrice() == null ? cardConfig.getOriginalPrice() : cardConfig.getPromotionPrice());
        yearCardOrderDO.setStatus(Boolean.FALSE);
        yearCardOrderDO.setYearCardStartTime(LocalDateTime.now());
        yearCardOrderDO.setYearCardEndTime(LocalDateTime.now().plusYears(1));
        yearCardOrderMapper.insert(yearCardOrderDO);

        createPayOrder(yearCardOrderDO, userIp, yearCardOrderProperties);

        return yearCardOrderDO;
    }

    private void createPayOrder(YearCardOrderDO yearCardOrder, String userIp, YearCardOrderProperties yearCardOrderProperties) {
        PayOrderCreateReqDTO payOrderCreateReqDTO = new PayOrderCreateReqDTO();
        payOrderCreateReqDTO.setMerchantOrderId(String.valueOf(yearCardOrder.getId()));
        payOrderCreateReqDTO.setAppId(yearCardOrderProperties.getAppId());
        payOrderCreateReqDTO.setUserIp(userIp);
        payOrderCreateReqDTO.setSubject("会员年卡");
        payOrderCreateReqDTO.setBody(payOrderCreateReqDTO.getSubject());
        payOrderCreateReqDTO.setPrice(yearCardOrder.getYearCardAmount().multiply(new BigDecimal("100")).intValue());
        payOrderCreateReqDTO.setExpireTime(addTime(yearCardOrderProperties.getPayExpireTime()));

        Long payOrderId = payOrderApi.createOrder(payOrderCreateReqDTO);

        // 更新到交易单上
        yearCardOrderMapper.updateById(new YearCardOrderDO().setId(yearCardOrder.getId()).setPayOrderId(payOrderId).setPayOrderStatus(0));
        yearCardOrder.setPayOrderId(payOrderId);
        yearCardOrder.setPayOrderStatus(0);
    }

    @Override
    public PageResult<YearCardOrderDO> getYearCardOrderPage(YearCardOrderPageReqVO pageReqVO) {
        return yearCardOrderMapper.selectPage(pageReqVO);
    }

    @Override
    public int expireOrder() {
        List<YearCardOrderDO> yearCardOrderDOS = yearCardOrderMapper.selectExpireOrder();
        if (CollUtil.isNotEmpty(yearCardOrderDOS)) {
            yearCardOrderDOS.forEach(e -> e.setStatus(Boolean.FALSE));
            yearCardOrderMapper.updateBatch(yearCardOrderDOS);
        }
        return yearCardOrderDOS.size();
    }

    @Override
    public int sendCoupon(LocalDate sendDate) {

        DictDataRespDTO dictDataRespDTO = dictDataApi.parseDictData(DictTypeConstants.YEAR_CARD_COUPON, DictTypeConstants.YEAR_CARD_COUPON_LABEL);
        if (dictDataRespDTO == null) {
            log.error("未配置年卡会员发券，请在字典中配置!");
            return 0;
        }
        // 1.获取未过期年卡会员购买记录
        List<YearCardOrderDO> yearCardOrderList = yearCardOrderMapper.selectNotExpireOrder();
        if (CollectionUtils.isAnyEmpty(yearCardOrderList)) {
            log.error("未查到未过期年卡会员购买记录");
            return 0;
        }
        List<Long> userIds = yearCardOrderList.stream().filter(item -> Objects.nonNull(item.getMemberId())).map(YearCardOrderDO::getMemberId).collect(Collectors.toList());
        // 2.获取已经发送券的用户
        List<CouponDO> couponList = couponMapper.selectHasSendList(Long.valueOf(dictDataRespDTO.getValue()), userIds, sendDate);
        if (!CollectionUtils.isAnyEmpty(couponList)) {
            List<Long> hasSendUserIds = couponList.stream().map(CouponDO::getUserId).distinct().collect(Collectors.toList());
            // 3.剔除已经发送的券用户
            userIds = userIds.stream().filter(item -> !hasSendUserIds.contains(item)).collect(Collectors.toList());
        }
        List<Long> sendIds = Lists.newArrayList();
        if (!CollectionUtils.isAnyEmpty(userIds)) {
            userIds.forEach(item -> {
                try {
                    couponApi.takeCoupon(Long.valueOf(dictDataRespDTO.getValue()), Sets.newHashSet(item), CouponTakeTypeEnum.ADMIN, Boolean.FALSE, null, null);
                    sendIds.add(item);
                    log.info("发送年卡优惠券成功,templateId[{}],userId[{}]",dictDataRespDTO.getValue(),item);
                } catch (NumberFormatException e) {
                    log.error("发送年卡优惠券失败,templateId[{}],userId[{}]",dictDataRespDTO.getValue(),item);
                }
            });
        }
        return sendIds.size();
    }

    public static void main(String[] args) {
        LocalDateTime minTime = LocalDateTime.now().with(LocalDateTime.MIN);
        LocalDateTime maxTime = LocalDateTime.now().with(LocalDateTime.MAX);
        System.out.println(minTime.toLocalDate());
        System.out.println(maxTime.toLocalDate());
    }
}